﻿//-----------------------------------------------------------------------
// <copyright file="GraphDataHandler.cs" company="Redmist">
//     Copyright (c) Damien T Smith. All rights reserved.
// </copyright>
// <author>Damien Smith</author>
//-----------------------------------------------------------------------

namespace Redmist.UsageMonitor.Model
{
    using System;

    /// <summary>Creates data suitable for displaying on a graph based on download data and view parameters.</summary>
    public class GraphDataHandler
    {
        /// <summary>An array of integers representing peak download data.</summary>
        private int[] peakDownloads;

        /// <summary>An array of integers representing off peak download data.</summary>
        private int[] offPeakDownloads;

        /// <summary>An array of integers representing peak upload data.</summary>
        private int[] peakUploads;

        /// <summary>An array of integers representing off peak upload data.</summary>
        private int[] offPeakUploads;

        /// <summary>The number of days of data to be shown on the graph.</summary>
        private int days;

        /// <summary>The ratio of reduced counted off peak usage.</summary>
        private int quotaRatio;

        /// <summary>A value indicating whether the graphs should show off peak usage as counted only.</summary>
        private bool countedOffPeak;

        /// <summary>A value indicating whether the graphs should show uploads and downloads at the same scale.</summary>
        private bool commonScale;

        /// <summary>Initializes a new instance of the GraphDataHandler class.</summary>
        /// <param name="peakDownloads">An array of integers representing peak download data.</param>
        /// <param name="offPeakDownloads">An array of integers representing off peak download data.</param>
        /// <param name="peakUploads">An array of integers representing peak upload data.</param>
        /// <param name="offPeakUploads">An array of integers representing off peak upload data.</param>
        /// <param name="days">The number of days of data to be shown on the graph.</param>
        /// <param name="historyView">A Redmist.UsageMonitor.Model.HistoryView indicating whether peak usage, off-peak usage or both should be shown on the graphs.</param>
        /// <param name="countedOffPeak">A value indicating whether the graphs should show off peak usage as counted only.</param>
        /// <param name="commonScale">A value indicating whether the graphs should show uploads and downloads at the same scale.</param>
        /// <param name="quotaRatio">The ratio of reduced counted off peak usage.</param>
        public GraphDataHandler(int[] peakDownloads, int[] offPeakDownloads, int[] peakUploads, int[] offPeakUploads, int days, HistoryView historyView, bool countedOffPeak, bool commonScale, int quotaRatio)
        {
            this.peakDownloads = peakDownloads;
            this.offPeakDownloads = offPeakDownloads;
            this.peakUploads = peakUploads;
            this.offPeakUploads = offPeakUploads;
            this.days = days;
            this.HistoryView = historyView;
            this.countedOffPeak = countedOffPeak;
            this.commonScale = commonScale;
            this.quotaRatio = quotaRatio;
        }

        /// <summary>Gets a Redmist.UsageMonitor.Model.HistoryView indicating whether peak usage, off-peak usage or both should be shown on the graphs.</summary>
        public HistoryView HistoryView { get; private set; }

        /// <summary>Gets the primary data for the download graph.</summary>
        public int[] DownloadPrimaryData { get; private set; }

        /// <summary>Gets the primary data for the upload graph.</summary>
        public int[] UploadPrimaryData { get; private set; }

        /// <summary>Gets the secondary data for the download graph.</summary>
        public int[] DownloadSecondaryData { get; private set; }

        /// <summary>Gets the secondary data for the upload graph.</summary>
        public int[] UploadSecondaryData { get; private set; }

        /// <summary>Gets the maximum Y-value for the download graph.</summary>
        public int DownloadGraphMaximum { get; private set; }

        /// <summary>Gets the maximum Y-value for the upload graph.</summary>
        public int UploadGraphMaximum { get; private set; }

        /// <summary>Generates the data for the graph based on the supplied parameters.</summary>
        public void CreateGraphData()
        {
            switch (this.HistoryView)
            {
                case HistoryView.Peak:
                    this.DownloadPrimaryData = this.GetEndArray(this.peakDownloads);
                    this.DownloadSecondaryData = new int[this.days];
                    this.UploadPrimaryData = this.GetEndArray(this.peakUploads);
                    this.UploadSecondaryData = new int[this.days];
                    this.DownloadGraphMaximum = this.FindMaximumValue(this.DownloadPrimaryData);
                    this.UploadGraphMaximum = this.FindMaximumValue(this.UploadPrimaryData);
                    break;
                case HistoryView.OffPeak:
                    this.DownloadPrimaryData = new int[this.days];
                    this.DownloadSecondaryData = this.GetEndArray(this.offPeakDownloads);
                    this.UploadPrimaryData = new int[this.days];
                    this.UploadSecondaryData = this.GetEndArray(this.offPeakUploads);
                    if (!this.countedOffPeak && this.quotaRatio != 1)
                    {
                        this.DownloadSecondaryData = this.MultiplyArray(this.DownloadSecondaryData);
                        this.UploadSecondaryData = this.MultiplyArray(this.UploadSecondaryData);
                    }

                    this.DownloadGraphMaximum = this.FindMaximumValue(this.DownloadSecondaryData);
                    this.UploadGraphMaximum = this.FindMaximumValue(this.UploadSecondaryData);
                    break;
                case HistoryView.Combined:
                    this.DownloadPrimaryData = this.GetEndArray(this.peakDownloads);
                    this.DownloadSecondaryData = this.GetEndArray(this.offPeakDownloads);
                    this.UploadPrimaryData = this.GetEndArray(this.peakUploads);
                    this.UploadSecondaryData = this.GetEndArray(this.offPeakUploads);
                    if (!this.countedOffPeak && this.quotaRatio != 1)
                    {
                        this.DownloadSecondaryData = this.MultiplyArray(this.DownloadSecondaryData);
                        this.UploadSecondaryData = this.MultiplyArray(this.UploadSecondaryData);
                    }

                    this.DownloadGraphMaximum = this.FindMaximumValue(this.SumArrays(this.DownloadPrimaryData, this.DownloadSecondaryData));
                    this.UploadGraphMaximum = this.FindMaximumValue(this.SumArrays(this.UploadPrimaryData, this.UploadSecondaryData));
                    break;
            }

            if (this.commonScale)
            {
                this.DownloadGraphMaximum = this.CalculateGreaterValue(this.DownloadGraphMaximum, this.UploadGraphMaximum);
                this.UploadGraphMaximum = this.DownloadGraphMaximum;
            }
        }

        /// <summary>Takes two arrays of integers which are the same length and sums them together.</summary>
        /// <param name="firstArray">The first array of integers.</param>
        /// <param name="secondArray">The second array of integers.</param>
        /// <returns>An array of integers representing a summed array of the two source arrays.</returns>
        private int[] SumArrays(int[] firstArray, int[] secondArray)
        {
            int[] summedArray = new int[firstArray.Length];
            for (int i = 0; i < firstArray.Length; i++)
            {
                summedArray[i] = firstArray[i] + secondArray[i];
            }

            return summedArray;
        }

        /// <summary>Takes two integers and returns the greater of the two.</summary>
        /// <param name="firstValue">The first integer.</param>
        /// <param name="secondValue">The second integer.</param>
        /// <returns>The larger of the two integers.</returns>
        private int CalculateGreaterValue(int firstValue, int secondValue)
        {
            if (firstValue > secondValue)
            {
                return firstValue;
            }

            return secondValue;
        }

        /// <summary>Gets the end of a source array based on the selected number of days to view for the graph.</summary>
        /// <param name="sourceArray">An array of integers representing the source array.</param>
        /// <returns>An array of integers representing the end of the source array.</returns>
        private int[] GetEndArray(int[] sourceArray)
        {
            int[] newArray = new int[this.days];
            Array.Copy(sourceArray, sourceArray.Length - this.days, newArray, 0, this.days);
            return newArray;
        }

        /// <summary>Calculates the maximum Y value for the graph based on the maximum value in the source data.</summary>
        /// <param name="array">An array of integers for which to find the maximum value.</param>
        /// <returns>A suitable maximum Y value for the graph based on the maximum value found in the supplied array.</returns>
        private int FindMaximumValue(int[] array)
        {
            int maximumValue = 0;
            for (int i = 0; i <= array.Length - 1; i++)
            {
                if (maximumValue < array[i])
                {
                    maximumValue = array[i];
                }
            }

            return maximumValue * 110 / 100;
        }

        /// <summary>Multiplies all integers in an array by the current quota ratio.</summary>
        /// <param name="array">An array of integers representing the values to be multiplied.</param>
        /// <returns>An array of integers representing the multiplied values.</returns>
        private int[] MultiplyArray(int[] array)
        {
            for (int i = 0; i < array.Length; i++)
            {
                array[i] *= this.quotaRatio;
            }

            return array;
        }
    }
}
